package com.zhaoziqian.web;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zhaoziqian.config.CodeEnum;
import com.zhaoziqian.config.EnvParam;
import com.zhaoziqian.config.StatusEnum;
import com.zhaoziqian.model.OrdererModel;
import com.zhaoziqian.model.PeerModel;
import com.zhaoziqian.model.RequestModel;
import com.zhaoziqian.model.SDKResponse;
import com.zhaoziqian.service.BlockService;
import com.zhaoziqian.service.Invocation;
import com.zhaoziqian.service.Network;
import com.zhaoziqian.utils.Param;
import com.zhaoziqian.utils.ReadNetworkConfig;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Properties;


/**
 * Created with fabric-cli
 * 作者： zhaoziqian
 * 时间： 2018/5/22  下午4:17
 * 功能：
 */
@Controller
public class MainController {

    private Logger logger = LoggerFactory.getLogger(MainController.class);

    @Autowired
    private Network network;

    @Autowired
    private Invocation invocation;

    @Autowired
    private BlockService blockService;

    @RequestMapping(value = "/chaincode", method = RequestMethod.POST)
    @ResponseBody
    public SDKResponse main(@RequestBody String model) {

        logger.debug("-------------------------------------------------------");
        logger.debug("\n {} \n", model);
        logger.debug("-------------------------------------------------------");

        RequestModel requestModel = JSON.parseObject(model, RequestModel.class);

        // 使用method 和 function 共同决定执行方法
        String method = requestModel.getMethod();
        String function = requestModel.getParams().getCtorMsg().getFunction();
        if (StringUtils.isBlank(method)) {
            return new SDKResponse(CodeEnum.ERROR.getCode(), StatusEnum.ERROR.getStatus(), "Method is empty.");
        }
        if (StringUtils.isBlank(function)) {
            return new SDKResponse(CodeEnum.ERROR.getCode(), StatusEnum.ERROR.getStatus(), "Function is empty.");
        }
        logger.info("The method is {} , and the function is {}", method, function);

        String org = EnvParam.ORG;
        String peer = EnvParam.PEER;
        String ordererOrg = EnvParam.ORDERER_ORG;
        String ordererId = EnvParam.ORDERERID;
        String chaincodePath = requestModel.getParams().getChaincodeID().getPath();
        String chaincodeVersion = "1.0";
        String channelName = requestModel.getParams().getChaincodeID().getChannelName();
        String chaincodeId = requestModel.getParams().getChaincodeID().getName();
        String[] args = requestModel.getParams().getCtorMsg().getArgs();

        SDKResponse res = null;

        if (StringUtils.equals(Param.OPERTATE_CHANNEL, method) && StringUtils.equals(Param.CREATE, function))
            method = Param.CREATE;

        if (StringUtils.equals(Param.OPERTATE_CHANNEL, method) && StringUtils.equals(Param.JOIN, function))
            method = Param.JOIN;

        if (StringUtils.equals(Param.DEPLOY, method) && StringUtils.equals(Param.INSTALL, function))
            method = Param.INSTALL;

        if (StringUtils.equals(Param.DEPLOY, method) && StringUtils.equals(Param.INSTANTIATE, function))
            method = Param.INSTANTIATE;

        switch (method) {
            case Param.CREATE:
                res = createChannel(ordererId, ordererOrg, org, channelName);
                break;
            case Param.JOIN:
                res = joinChannel(ordererOrg, ordererId, peer, org, channelName);
                break;
            case Param.INSTALL:
                res = installChainCode(ordererOrg, ordererId, peer, org, channelName, chaincodePath, chaincodeVersion, chaincodeId);
                break;
            case Param.INSTANTIATE:
                res = instantiateChainCode(ordererOrg, ordererId, peer, org, channelName, chaincodePath, chaincodeVersion, chaincodeId, args);
                break;
            case Param.INVOKE:
                res = invokeChainCode(ordererOrg, ordererId, peer, org, chaincodeId, chaincodeVersion, function, args, channelName);
                break;
            case Param.QUERY:
                // TODO 查询方法的返回值还需要修改
                res = queryChainCode(ordererOrg, ordererId, peer, org, chaincodeId, chaincodeVersion, function, args, channelName);
                String result = JSON.parseArray(res.getMsg()).getString(0);
                res = SDKResponse.OK(JSON.parseObject(result).getString("result"));
                break;
            case Param.BLOCK:
                res = queryBlockChainInfo(ordererOrg,ordererId,org,peer,channelName,function,analyticArgsFromJson(args));
                break;
            default:
                logger.error("Can't find method about {} !!", method);
                res = SDKResponse.ERROR;
                break;
        }

        logger.info("-------------------------------------------------------");
        logger.info("\n {} \n", res.getStatus());
        logger.info("-------------------------------------------------------");

        return res;
    }


    private SDKResponse createChannel(String ordererId, String ordererOrg, String org, String channelName) {
        try {
            network.createChannel(ordererId, ordererOrg, org, channelName);
        } catch (Exception e) {
            e.printStackTrace();
            return new SDKResponse(CodeEnum.ERROR.getCode(), StatusEnum.CREATE_CHANNEL_ERROR.getStatus(), e.getMessage());
        }
        return SDKResponse.OK("create channel done.");
    }

    private SDKResponse joinChannel(String ordererOrg, String ordererId, String peerId, String org, String channelName) {
        try {
            network.joinChannel(ordererOrg, ordererId, peerId, org, channelName);
        } catch (Exception e) {
            e.printStackTrace();
            return new SDKResponse(CodeEnum.ERROR.getCode(), StatusEnum.JOIN_CHANNEL_ERROR.getStatus(), e.getMessage());
        }
        return SDKResponse.OK(org + "." + peerId + " join channel done.");
    }

    private SDKResponse installChainCode(String ordererOrg, String ordererId, String peerId, String org, String channelName, String chainCodePath, String chaincodeVersion, String chaincodeId) {
        SDKResponse res = null;
        try {
            res = network.installChainCode(ordererOrg, ordererId, peerId, org, channelName, chainCodePath, chaincodeVersion, chaincodeId);
        } catch (Exception e) {
            e.printStackTrace();
            return new SDKResponse(CodeEnum.ERROR.getCode(), StatusEnum.INSTALL_CHAINCODE_ERROR.getStatus(), e.getMessage());
        }
        return res;
    }

    private SDKResponse instantiateChainCode(String ordererOrg, String ordererId, String peerId, String org, String channelName, String chainCodePath, String chaincodeVersion, String chaincodeId, String[] args) {
        SDKResponse res = null;
        try {
            res = network.instantiateChainCode(ordererOrg, ordererId, peerId, org, channelName, chainCodePath, chaincodeVersion, chaincodeId, args);
        } catch (Exception e) {
            e.printStackTrace();
            return new SDKResponse(CodeEnum.ERROR.getCode(), StatusEnum.INSTANTIATE_CHAINCODE_ERROR.getStatus(), e.getMessage());
        }
        return res;
    }

    private SDKResponse invokeChainCode(String ordererOrg, String ordererId, String peerId, String org, String chainCodeId, String chainCodeVersion, String function, String[] args, String channelName) {
        SDKResponse res = null;
        try {
            res = invocation.invokeChainCode(ordererOrg, ordererId, peerId, org, chainCodeId, chainCodeVersion, function, args, channelName);
        } catch (Exception e) {
            e.printStackTrace();
            return new SDKResponse(CodeEnum.ERROR.getCode(), StatusEnum.INVOKE_CHAINCODE_ERROR.getStatus(), e.getMessage());
        }
        return res;
    }

    private SDKResponse queryChainCode(String ordererOrg, String ordererId, String peerId, String org, String chainCodeId, String chainCodeVersion, String function, String[] args, String channelName) {
        SDKResponse res = null;
        try {
            res = invocation.queryChainCode(ordererOrg, ordererId, peerId, org, chainCodeId, chainCodeVersion, function, args, channelName);
        } catch (Exception e) {
            e.printStackTrace();
            return new SDKResponse(CodeEnum.ERROR.getCode(), StatusEnum.QUERY_CHAINCODE_ERROR.getStatus(), e.getMessage());
        }
        return res;
    }

    private SDKResponse queryBlockChainInfo(String ordererOrg, String ordererId, String org, String peerId, String channelName, String function, Properties prop) {
        SDKResponse res = null;
        try {
            switch (function) {
                case "getBlock":
                    res = blockService.getBlock(ordererOrg, ordererId, org, peerId, channelName, prop.getProperty("blockNumber"));
                    break;
                case "queryTransaction":
                    res = blockService.queryTransaction(ordererOrg,ordererId,org,peerId,channelName,prop.getProperty("txId"));
                    break;
                case "queryInfo":
                    res = blockService.queryInfo(ordererOrg,ordererId,org,peerId,channelName);
                    break;
                case "queryInstalledChaincodes":
                    res = blockService.queryInstalledChaincodes(ordererOrg,ordererId,org,peerId,channelName);
                    break;
                case "queryInstantiatedChaincodes":
                    res = blockService.queryInstantiatedChaincodes(ordererOrg,ordererId,org,peerId,channelName);
                    break;
                case "queryChannels":
                    res = blockService.queryChannels(ordererOrg,ordererId,org,peerId,channelName);
                    break;
                case "getPeers":
                    res = blockService.getPeers(ordererOrg,ordererId,org,peerId,channelName);
                    break;
                default:
                    logger.error("Can't find function about {} !!", function);
                    res = SDKResponse.ERROR;
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new SDKResponse(CodeEnum.ERROR.getCode(), StatusEnum.QUERY_CHAINCODE_ERROR.getStatus(), e.getMessage());
        }

        return res;
    }


    @RequestMapping(value = "/network-config", method = RequestMethod.POST)
    @ResponseBody
    public Object networkConfig(@RequestBody String model) {
        JSONObject param = JSON.parseObject(model);
        logger.info(param.toJSONString());

        String org = param.getString("org");
        String ordererId = param.getString("ordererId");
        String peerId = param.getString("peerId");

        OrdererModel ordererModel = ReadNetworkConfig.getOrderer(org, ordererId);

        PeerModel peerModel = ReadNetworkConfig.getPeer(org, peerId);

        logger.info(ordererModel.toString());
        logger.info(peerModel.toString());
        return peerModel;
//        return SDKResponse.SUCCESS;
    }

    /**
     * 为了不修改json报文格式的不就方法，将block操作的的参数放在 args 的第一个参数中，格式为json格式
     * @param args
     * @return
     */
    private Properties analyticArgsFromJson(String[] args) {
        Properties prop = new Properties();
        if (args.length < 1) return null;
        JSONObject object = JSON.parseObject(args[0]);
        object.keySet().forEach(key -> {
            prop.setProperty(key,object.getString(key));
        });
        return prop;
    }

}
